﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Xml.Serialization;

namespace Beeper
{
    public enum PlaybackState
    {
        Playing,
        Paused,
        Stopped
    }

    public class BeepSequence
    {
        #region Fields
        private delegate void PlaybackDelegate();

        private PlaybackDelegate playDelegate;

        private AsyncCallback playSequenceAsyncCallback;

        private bool stopRequested;
        private bool pauseRequested;
        #endregion

        #region Events
        public event EventHandler PlaybackStatusChanged;
        #endregion

        public BeepSequence()
        {
            this.playSequenceAsyncCallback = new AsyncCallback(EndPlaySequence);
        }

        #region Properties
        private List<Beep> beeps = new List<Beep>();
        public List<Beep> Beeps
        {
            get { return beeps; }
        }

        private PlaybackState playbackStatus = PlaybackState.Stopped;
        [XmlIgnore]
        public PlaybackState PlaybackStatus
        {
            get { return playbackStatus; }
            private set
            {
                if (playbackStatus != value)
                {
                    playbackStatus = value;
                    OnPlaybackStatusChanged(EventArgs.Empty);
                }
            }
        }
        #endregion

        #region Playback

        #region Play
        /// <summary>
        /// Begins or resumes playback of the beep sequence.
        /// </summary>
        public virtual void Play()
        {
            if (this.PlaybackStatus == PlaybackState.Paused)
                pauseRequested = false;
            else if (this.PlaybackStatus == PlaybackState.Stopped)
            {
                this.BeginPlaySequence();
            }
        }

        /// <summary>
        /// Begins playback of the beep sequence asynchronously.
        /// </summary>
        /// <remarks>If another asynchronous playback operation is already in progress, the request will be ignored.</remarks>
        protected virtual void BeginPlaySequence()
        {
            if (playDelegate == null)
            {
                PlaybackDelegate del = new PlaybackDelegate(PlaySequence);
                playDelegate = del;
                del.BeginInvoke(playSequenceAsyncCallback, null);
            }
        }

        /// <summary>
        /// Waits for the current asynchronous play operation to complete.
        /// </summary>
        /// <param name="iar">The IAsyncResult of the asynchronous operation.</param>
        protected virtual void EndPlaySequence(IAsyncResult iar)
        {
            if (playDelegate != null)
            {
                playDelegate.EndInvoke(iar);
                playDelegate = null;
            }
        }

        /// <summary>
        /// Plays the beep sequence.
        /// </summary>
        protected virtual void PlaySequence()
        {
            foreach (Beep beep in beeps)
            {
                while (pauseRequested)
                {
                    if (stopRequested)
                        break;
                    this.PlaybackStatus = PlaybackState.Paused;
                    Thread.Sleep(10);
                }

                if (stopRequested)
                    break;

                this.PlaybackStatus = PlaybackState.Playing;

                beep.Play();
            }
            pauseRequested = false;
            stopRequested = false;
            this.PlaybackStatus = PlaybackState.Stopped;
        }
        #endregion

        #region Stop
        /// <summary>
        /// Stops playback of the beep sequence.
        /// </summary>
        public virtual void Stop()
        {
            if (this.PlaybackStatus != PlaybackState.Stopped)
                stopRequested = true;
        }
        #endregion

        #region Pause
        /// <summary>
        /// Pauses playback of the beep sequence.
        /// </summary>
        public virtual void Pause()
        {
            if (this.PlaybackStatus == PlaybackState.Playing)
                pauseRequested = true;
            else if (this.PlaybackStatus == PlaybackState.Paused)
                pauseRequested = false;
        }
        #endregion

        #endregion

        protected virtual void OnPlaybackStatusChanged(EventArgs e)
        {
            EventHandler handler = this.PlaybackStatusChanged;
            if (handler != null)
                handler(this, e);
        }

        public void Save(string path)
        {
            BeepXmlWriter writer = new BeepXmlWriter();
            writer.WriteFile(this, path);
        }

        public static BeepSequence FromFile(string path)
        {
            BeepXmlReader reader = new BeepXmlReader();
            return reader.ReadFile(path);
        }
    }
}
